package com.panfeng.xcloud.scheduler.service.impl;

import com.panfeng.xcloud.common.core.constants.BlConstants;
import com.panfeng.xcloud.common.core.enums.ChainTypeEnum;
import com.panfeng.xcloud.common.core.enums.CoinTypeEnum;
import com.panfeng.xcloud.common.core.enums.RechargeLogTypeEnum;
import com.panfeng.xcloud.common.core.enums.WalletAssetsFlowTypeEnum;
import com.panfeng.xcloud.dao.assets.entity.RechargeLog;
import com.panfeng.xcloud.dao.assets.entity.UserWalletInfo;
import com.panfeng.xcloud.dao.assets.entity.WalletAssetsFlow;
import com.panfeng.xcloud.scheduler.dao.mapper.RechargeLogMapperExt;
import com.panfeng.xcloud.scheduler.dao.mapper.UserWalletInfoMapperExt;
import com.panfeng.xcloud.scheduler.dao.mapper.WalletAssetsFlowMapperExt;
import com.panfeng.xcloud.scheduler.dto.DataTRX;
import com.panfeng.xcloud.scheduler.dto.Result;
import com.panfeng.xcloud.scheduler.service.IFeignService;
import com.panfeng.xcloud.scheduler.service.IScanTrxBlockService;
import com.panfeng.xcloud.scheduler.util.OrderNoGenerate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import trontool.Address;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

@Service
@Slf4j
public class ScanTrxBlockServiceImpl implements IScanTrxBlockService {

    @Autowired
    private TronTrc20Service tronTrc20Service;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private RechargeLogMapperExt rechargeLogMapperExt;

    @Autowired
    private UserWalletInfoMapperExt userWalletInfoMapperExt;

    @Autowired
    private WalletAssetsFlowMapperExt walletAssetsFlowMapperExt;

    @Autowired
    private IFeignService feignService;

    /**
     * 扫描区块，根据时间戳
     */
    //1.获取当前系统区块查询时间戳
    //2.当前系统区块时间戳最大值和最新的区块高度时间戳对比,相差大于3分钟就循环，每次累加1分钟的时间
    //3.调用API获取所有交易
    //4.将to_address和用户地址相同的交易存到RechargeLog表
    @Override
    public void scanBlockTask() {
        BigInteger newestBlockNum = getNewestBlockNum().subtract(BigInteger.valueOf(20));
        BigInteger from = getScannedBlockNum().add(BigInteger.ONE);
        while (newestBlockNum.compareTo(from) > 0) {
            doScanUsdt(from);
            from = getScannedBlockNum().add(BigInteger.ONE);
        }
    }

    //获取当前最新区块
    private BigInteger getNewestBlockNum() {
        String redisValue = (String) redisTemplate.opsForValue().get(BlConstants.REDIS_KEY_NEWEST_BLOCK_NUM);
        if (redisValue == null) {
            log.error("获取最新块失败,初始化");
            return BigInteger.ZERO;
        }
        return new BigInteger(redisValue);
    }

    //扫描任务
    private void doScanUsdt(BigInteger from) {
        List<String> contractList = Arrays.asList(
                BlConstants.UsdtContractAddress
        );
        for (String contractAddress : contractList) {
            List<DataTRX> trc20UsdtTxs = tronTrc20Service.getTrc20UsdtTxs(contractAddress, from);
            handle(trc20UsdtTxs, contractAddress);
        }
        log.info("TRON 区块扫描进度 from:{}", from);
        updateScannedBlockNum(from);
    }

    //获取已扫描的块
    private BigInteger getScannedBlockNum() {
        String redisValue = (String) redisTemplate.opsForValue().get(BlConstants.REDIS_KEY_SCANNED_BLOCK_NUM);
        if (redisValue == null) {
            log.error("获取已扫描块失败，将从前500块开始扫描");
            return getNewestBlockNum().subtract(new BigInteger("500"));

        }
        log.info("区块扫描进度：" + redisValue);
        return new BigInteger(redisValue);
    }

    //更新已扫描的块
    private void updateScannedBlockNum(BigInteger from) {
        redisTemplate.opsForValue().set(BlConstants.REDIS_KEY_SCANNED_BLOCK_NUM, from.toString());
    }

    //处理充值
    @Transactional(rollbackFor = Exception.class)
    public void handle(List<DataTRX> txs, String contraAddress) {
        String coinName;
        if (contraAddress.equals(BlConstants.UsdtContractAddress)) {
            coinName = CoinTypeEnum.USDT.getCode();
        } else {
            return;
        }
        for (DataTRX tx : txs) {
            Result result = tx.getResult();

            String resFrom = result.getFrom();
            String resTo = result.getTo();
            resFrom = Address.encode("41" + resFrom.substring(2));
            resTo = Address.encode("41" + resTo.substring(2));
            String resValue = result.getValue();

            BigDecimal amount = BigDecimal.ZERO;

            amount = parseUsdtAmount(resValue, BlConstants.UsdtDecimal);

            Long blockNumber = tx.getBlock_number();
            Long blockTimestamp = tx.getBlock_timestamp();

            String hash = tx.getTransaction_id();

            int count = rechargeLogMapperExt.countByHash(hash);
            if (count > 0) {
                continue;
            }

            UserWalletInfo req = new UserWalletInfo();
            req.setAddress(resTo);
            UserWalletInfo userWalletInfo = userWalletInfoMapperExt.selectOne(req);

            if (userWalletInfo == null) {
                continue;
            }

            RechargeLog logEntity = new RechargeLog();
            logEntity.setChainName(ChainTypeEnum.TRON.getCode());
            logEntity.setCoinName(coinName);
            logEntity.setFromAddress(resFrom);
            logEntity.setToAddress(resTo);
            logEntity.setHash(hash);
            logEntity.setAmount(amount);
            logEntity.setBlockNum(blockNumber);
            logEntity.setState(RechargeLogTypeEnum.RECHARGE_SUCCESS.getCode());
            logEntity.setUserId(userWalletInfo.getUserId());
            rechargeLogMapperExt.insertSelective(logEntity);

            //往wallet_assets_flow插入记录
            WalletAssetsFlow walletAssetsFlow = new WalletAssetsFlow();
            walletAssetsFlow.setFlowOrderId(OrderNoGenerate.getOrderIdByTime());
            walletAssetsFlow.setUserId(userWalletInfo.getUserId());
            walletAssetsFlow.setFromAddress(resFrom);
            walletAssetsFlow.setToAddress(resTo);
            walletAssetsFlow.setCreateTime(new Date());
            walletAssetsFlow.setCoin(coinName);
            walletAssetsFlow.setAmount(amount);
            walletAssetsFlow.setType(WalletAssetsFlowTypeEnum.RECHARGE.getCode());
            walletAssetsFlow.setTxHash(hash);

            walletAssetsFlowMapperExt.insertSelective(walletAssetsFlow);

            //更新user_wallet_info表可用余额
            UserWalletInfo req1 = new UserWalletInfo();
            req1.setUserId(userWalletInfo.getUserId());
            req1.setChainName(ChainTypeEnum.TRON.getCode());
            req1.setCoinName(coinName);
            req1.setAddress(resTo);
            req1.setAvaliableBalance(amount);
            userWalletInfoMapperExt.updateUserWalletInfo(req1);

            log.info("USDT/TRON新增充值记录：{}", logEntity);
        }
    }

    //格式化交易金额
    private BigDecimal parseUsdtAmount(String val, int decimal) {
        if (decimal == BlConstants.UsdtDecimal) {
            return new BigDecimal(val)
                    .divide(BlConstants.USDT_DECIMAL, BlConstants.UsdtDecimal, BigDecimal.ROUND_DOWN)
                    .stripTrailingZeros();
        } else {
            return null;
        }
    }

}
